﻿using System.Linq;
using UnityEngine;
using LuaInterface;
#if UNITY_EDITOR
using UnityEditor;
using System.Collections.Generic;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
#endif

namespace WentStateMachine
{
    [State(DisplayName = "LuaState")]
    public class LuaStateBehaviour : StateBehaviour
    {
        public static string[] LuaStates;

        public string ScriptGroup = "LuaScripts";
        public string StatePrefix = "";
        public string StatePostfix = "State.lua";
        public string LuaScriptName;
        private string scriptNameWithoutExtension;
        private LuaState luaState;
        private LuaTable luaBehaviour;

        public override void OnWentStateEnter(IStateMachineController controller, IStateInfo state, int layerIndex)
        {
            if (luaBehaviour == null)
                createState((LuaState)controller.GetGlobalParam("LuaState"));

            if (luaBehaviour == null)
                return;

            luaBehaviour.Call("OnWentStateEnter", luaBehaviour, controller, state, layerIndex);
        }

        public override void OnWentStateUpdate(IStateMachineController controller, IStateInfo state, int layerIndex)
        {
            if (luaBehaviour == null)
                createState((LuaState)controller.GetGlobalParam("LuaState"));

            if (luaBehaviour == null)
                return;

            luaBehaviour.Call("OnWentStateUpdate", luaBehaviour, controller, state, layerIndex);
        }

        public override void OnWentStateExit(IStateMachineController controller, IStateInfo state, int layerIndex)
        {
            if (luaBehaviour == null)
                createState((LuaState)controller.GetGlobalParam("LuaState"));

            if (luaBehaviour == null)
                return;

            luaBehaviour.Call("OnWentStateExit", luaBehaviour, controller, state, layerIndex);
        }

        private void createState(LuaState luaStatestate)
        {
            if (string.IsNullOrEmpty(LuaScriptName))
                return;

            this.luaState = luaStatestate;
            if (scriptNameWithoutExtension == null)
                scriptNameWithoutExtension = LuaScriptName.Substring(0, LuaScriptName.LastIndexOf('.'));
            luaBehaviour = luaState.Require<LuaTable>(scriptNameWithoutExtension);
            LuaFunction func = luaBehaviour.GetLuaFunction("New");
            func.BeginPCall();
            func.PCall();
            luaBehaviour = luaState.CheckLuaTable(luaState.LuaGetTop());
            func.EndPCall();
            luaState.CheckTop();
        }

#if UNITY_EDITOR
        private GUIContent simpleName;
        private GUIContent[] listContent;
        private int selectIndex = -1;

        public override void OnStateGUI()
        {
            EditorGUILayout.BeginVertical();
            {
                ScriptGroup = EditorGUILayout.TextField("ScriptGroup", ScriptGroup);
                StatePrefix = EditorGUILayout.TextField("StatePrefix", StatePrefix);
                StatePostfix = EditorGUILayout.TextField("StatePostfix", StatePostfix);

                scanLuaStateBehaviour();

                if (selectIndex == -1 && LuaScriptName != null && listContent != null)
                    findNameIndex(listContent, LuaScriptName);
                else
                {
                    var select = EditorGUILayout.Popup(selectIndex, listContent);
                    if(select != selectIndex)
                    {
                        LuaScriptName = listContent[select].text;
                        selectIndex = select;
                    }
                }
                    
                if (LuaScriptName != null)
                {
                    if (simpleName == null && LuaScriptName != null)
                        simpleName = new GUIContent(LuaScriptName.Contains('/') ? LuaScriptName.Substring(LuaScriptName.LastIndexOf('/') + 1) : LuaScriptName);
                    EditorGUILayout.LabelField(simpleName);
                }
            }
            EditorGUILayout.EndVertical();
        }

        public string GetScriptNameWithoutExtension()
        {
            return LuaScriptName.Substring(0, LuaScriptName.LastIndexOf('.'));
        }

        int findNameIndex(GUIContent[] names, string target)
        {
            for (int i = 0; i < names.Length; i++)
            {
                if (names[i].text == target)
                    return i;
            }
            return 0;
        }

        private void scanLuaStateBehaviour()
        {
            if (GUILayout.Button("Scan LuaSateBehaviour") || listContent == null)
            {
                while (true)
                {
                    if (string.IsNullOrEmpty(ScriptGroup))
                    {
                        EditorGUILayout.HelpBox("Must input a AddressableAsset group name,", MessageType.Warning);
                        break;
                    }

                    List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>(AddressableUtil.GetAllEntryInGroup(ScriptGroup));

                    if (entries.Count == 0)
                    {
                        EditorGUILayout.HelpBox($"Group {ScriptGroup} not exist or is empty.", MessageType.Warning);
                        break;
                    }

                    List<string> targets = filterEntries(entries);

                    if (targets.Count == 0)
                    {
                        EditorGUILayout.HelpBox($"Can not find script address start with {StatePrefix} and end with {StatePostfix} in group {ScriptGroup}.", MessageType.Warning);
                        break;
                    }

                    listContent = new GUIContent[targets.Count];

                    for (int i = 0; i < targets.Count; i++)
                    {
                        var target = targets[i];
                        listContent[i] = new GUIContent(target);
                    }
                    break;
                }
            }
        }

        private List<string> filterEntries(List<AddressableAssetEntry> entries)
        {
            List<string> targets = new List<string>();

            foreach(var entry in entries)
            {
                if (!string.IsNullOrEmpty(StatePrefix))
                {
                    if (!entry.address.StartsWith(StatePrefix))
                        continue;
                }

                if (!string.IsNullOrEmpty(StatePostfix))
                {
                    if (!entry.address.EndsWith(StatePostfix))
                        continue;
                }
                targets.Add(entry.address);
            }

            return targets;
        }
#endif
    }
}
